const fs = require("fs");
const path = require("path");
const mime = require("mime-types");
const exiftool = require("exiftool-vendored").exiftool;
const tinify = require("tinify");
const chalk = require("chalk");
const Keys = require("../utils/keys");
const formatBytes = require("../utils/index").formatBytes;

const uncompressedImages = [];

async function readDirFiles(dir) {
  const pathname = dir ?? process.cwd();
  const files = fs.readdirSync(pathname);

  const promises = files.map((file) => {
    const filename = path.join(pathname, file);
    const data = fs.statSync(filename);

    if (data.isFile()) {
      return fileFilter(filename);
    } else if (data.isDirectory()) {
      return readDirFiles(filename);
    }
  });

  try {
    await Promise.all(promises);
  } catch (err) {
    //...
  }
}

async function fileFilter(filename) {
  const contentType = mime.lookup(filename);
  const supportList = ["image/jpeg", "image/png", "image/webp"];

  if (typeof contentType === "string" && supportList.includes(contentType)) {
    return exiftool
      .read(filename)
      .then((tags) => {
        if (tags.XPComment !== "TinyPngCompressed") {
          uncompressedImages.push(filename);
        }
      })
      .catch((err) => {
        //...
      });
  }
}

async function compressImages() {
  const keys = Keys.getKeys();

  if (keys.length === 0) {
    console.log(chalk.blueBright("请添加Key"));
    return;
  }

  try {
    await getKey();

    const list = [];

    const promises = uncompressedImages.map((filename) => {
      const rawFileStat = fs.statSync(filename);

      return tinify
        .fromFile(filename)
        .toFile(filename)
        .then(() => {
          const fileStat = fs.statSync(filename);
          const ratio = Math.floor(
            (1 - fileStat.size / rawFileStat.size) * 100
          );

          console.log(
            chalk.greenBright(
              `${filename} 压缩完成 [${ratio}% ${formatBytes(
                rawFileStat.size
              )} -> ${formatBytes(fileStat.size)}]`
            )
          );

          return exiftool
            .write(filename, { XPComment: "TinyPngCompressed" }, [
              "-overwrite_original",
            ])
            .then(
              (data) => {
                // console.log("write success", data);
              },
              (err) => {
                // console.log("write fail", err);
              }
            );
        })
        .catch((err) => {
          if (err instanceof tinify.AccountError) {
            // Verify your API key and account limit.
            list.push(filename);
          } else {
            console.log(chalk.redBright(`${filename} 压缩失败，服务器故障`));
          }
        });
    });

    await Promise.allSettled(promises);

    // 存在因Key授权问题导致未压缩的图片，重新查询Key并压缩
    if (list.length) {
      console.log(chalk.blueBright("换Key压缩..."));
      uncompressedImages = list;
      await compressImages();
    }
  } catch (err) {
    console.log(chalk.redBright(err));
  }
}

function getKey() {
  return new Promise((resolve, reject) => {
    const key = Keys.selectKey();

    if (!key) {
      return reject("暂无可用Key");
    }

    tinify.key = key;

    tinify.validate((err) => {
      if (err) {
        console.log(
          chalk.redBright(
            `Key ${key} 验证失败，原因可能为Key无效或当月压缩已超限（免费500张/月）`
          )
        );
        resolve(getKey());
      } else {
        resolve();
      }
    });
  });
}

async function compress() {
  console.log(chalk.blueBright("读取文件..."));
  await readDirFiles();
  console.log(chalk.blueBright("读取文件完成"));

  if (uncompressedImages.length) {
    console.log(chalk.blueBright("开始压缩..."));
    await compressImages();
    console.log(chalk.blueBright("压缩完成"));
  } else {
    console.log(chalk.greenBright("没有需要压缩的图片"));
  }

  exiftool.end();
  console.log(chalk.bold.cyanBright("流程结束"));
}

module.exports = compress;

if (require.main === module) {
  compress().catch((err) => {
    console.error(err);
    process.exit(-1);
  });
}
